/* 
   Copyright 2011 Rolf Kulemann, Pascal Bockhorn

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */

package cn.imeth.log4j;

import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.RollingFileAppender;
import org.apache.log4j.helpers.LogLog;

import java.io.IOException;

/**
 * Configures the Log4j logging framework. See <a href=
 * "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html"
 * >Patterns</a> for pattern layout.
 *
 * @author Rolf Kulemann
 */
public class LogConfigurator {

    private static String FILE_PATTERN = "%d{yyy MMM dd HH:mm:ss}[%p:%C] %m%n";
    private static String LOG_CAT_PATTERN = "%m%n";

    private Level level = Level.DEBUG;
    private String fileName = "android-log4j.log";
    private Logger logger = Logger.getRootLogger();

    private int maxBackupSize = 5;
    private long maxFileSize = 512 * 1024;

    private boolean immediateFlush = true;
    /**
     * 是否使用Android的LogCat输出源
     */
    private boolean useLogCatAppender = true;
    /**
     * 是否使用保存文件记录的输出源
     */
    private boolean useFileAppender = true;
    /**
     * 是否重新注册
     */
    private boolean resetConfiguration = true;
    /**
     * 是否输出内部调试信息
     */
    private boolean internalDebugging = true;

    public LogConfigurator() {
    }

    public LogConfigurator(final String fileName) {
        setFileName(fileName);
    }

    /**
     * @param fileName Name of the log file
     * @param level Log level for the root logger
     */
    public LogConfigurator(final String fileName, final Level level) {
        this(fileName);
        setLevel(level);
    }

    /**
     * @param fileName Name of the log file
     * @param level Log level for the root logger
     * @param filePattern Log pattern for the file appender
     */
    public LogConfigurator(final String fileName, final Level level, final String filePattern) {
        this(fileName);
        setLevel(level);
        setFilePattern(filePattern);
    }

    /**
     * @param fileName Name of the log file
     * @param maxBackupSize Maximum number of backed up log files
     * @param maxFileSize Maximum size of log file until rolling
     * @param filePattern Log pattern for the file appender
     * @param level Log level for the root logger
     */
    public LogConfigurator(final String fileName, final int maxBackupSize, final long maxFileSize,
            final String filePattern, final Level level) {
        this(fileName, level, filePattern);
        setMaxBackupSize(maxBackupSize);
        setMaxFileSize(maxFileSize);
    }

    public void configure() {

        LogLog.setInternalDebugging(isInternalDebugging());

        LogLog.debug("Init Logger: " + logger.getName());

        if (isResetConfiguration()) {
            LogManager.getLoggerRepository().resetConfiguration();
        }

        if (isUseFileAppender()) {
            configureFileAppender();
        }

        if (isUseLogCatAppender()) {
            configureLogCatAppender();
        }

        logger.setLevel(getLevel());
    }

    private void configureFileAppender() {

        final RollingFileAppender rollingFileAppender;
        final Layout fileLayout = new PatternLayout(getFilePattern());

        try {
            rollingFileAppender = new RollingFileAppender(fileLayout, getFileName());
        } catch (final IOException e) {
            throw new RuntimeException("Exception configuring log system", e);
        }

        rollingFileAppender.setMaxBackupIndex(getMaxBackupSize());
        rollingFileAppender.setMaximumFileSize(getMaxFileSize());
        rollingFileAppender.setImmediateFlush(isImmediateFlush());

        logger.addAppender(rollingFileAppender);
    }

    private void configureLogCatAppender() {

        final Layout logCatLayout = new PatternLayout(getLogCatPattern());
        final LogCatAppender logCatAppender = new LogCatAppender(logCatLayout);

        logger.addAppender(logCatAppender);
    }

    /**
     * Return the log level of the root logger
     *
     * @return Log level of the root logger
     */
    public Level getLevel() {
        return level;
    }

    /**
     * Sets log level for the root logger
     *
     * @param level Log level for the root logger
     */
    public void setLevel(final Level level) {
        this.level = level;
    }

    public String getFilePattern() {
        return FILE_PATTERN;
    }

    public void setFilePattern(final String filePattern) {
        LogConfigurator.FILE_PATTERN = filePattern;
    }

    public String getLogCatPattern() {
        return LOG_CAT_PATTERN;
    }

    public void setLogCatPattern(final String logCatPattern) {
        LogConfigurator.LOG_CAT_PATTERN = logCatPattern;
    }

    public void setLogger(Logger logger) {
        this.logger = logger;
    }

    /**
     * Returns the name of the log file
     *
     * @return the name of the log file
     */
    public String getFileName() {
        return fileName;
    }

    /**
     * Sets the name of the log file
     *
     * @param fileName Name of the log file
     */
    public void setFileName(final String fileName) {
        this.fileName = fileName;
    }

    /**
     * Returns the maximum number of backed up log files
     *
     * @return Maximum number of backed up log files
     */
    public int getMaxBackupSize() {
        return maxBackupSize;
    }

    /**
     * Sets the maximum number of backed up log files
     *
     * @param maxBackupSize Maximum number of backed up log files
     */
    public void setMaxBackupSize(final int maxBackupSize) {
        this.maxBackupSize = maxBackupSize;
    }

    /**
     * Returns the maximum size of log file until rolling
     *
     * @return Maximum size of log file until rolling
     */
    public long getMaxFileSize() {
        return maxFileSize;
    }

    /**
     * Sets the maximum size of log file until rolling
     *
     * @param maxFileSize Maximum size of log file until rolling
     */
    public void setMaxFileSize(final long maxFileSize) {
        this.maxFileSize = maxFileSize;
    }

    public boolean isImmediateFlush() {
        return immediateFlush;
    }

    public void setImmediateFlush(final boolean immediateFlush) {
        this.immediateFlush = immediateFlush;
    }

    /**
     * Returns true, if FileAppender is used for logging
     *
     * @return True, if FileAppender is used for logging
     */
    public boolean isUseFileAppender() {
        return useFileAppender;
    }

    /**
     * @param useFileAppender the useFileAppender to set
     */
    public void setUseFileAppender(final boolean useFileAppender) {
        this.useFileAppender = useFileAppender;
    }

    /**
     * Returns true, if LogcatAppender should be used
     *
     * @return True, if LogcatAppender should be used
     */
    public boolean isUseLogCatAppender() {
        return useLogCatAppender;
    }

    /**
     * If set to true, LogCatAppender will be used for logging
     *
     * @param useLogCatAppender If true, LogCatAppender will be used for logging
     */
    public void setUseLogCatAppender(final boolean useLogCatAppender) {
        this.useLogCatAppender = useLogCatAppender;
    }

    public void setResetConfiguration(boolean resetConfiguration) {
        this.resetConfiguration = resetConfiguration;
    }

    /**
     * Resets the log4j configuration before applying this configuration.
     * Default is true.
     *
     * @return True, if the log4j configuration should be reset before applying
     *         this configuration.
     */
    public boolean isResetConfiguration() {
        return resetConfiguration;
    }

    public void setInternalDebugging(boolean internalDebugging) {
        this.internalDebugging = internalDebugging;
    }

    public boolean isInternalDebugging() {
        return internalDebugging;
    }

}
